跳到主要内容

Subcommand Groups

You read before how to create a program with Commands.

Now we'll see how to create a CLI program with commands that have their own subcommands. Also known as command groups.

Add Typer

举个例子,现在有两个命令程序 items.py 和 user.py 分别有两个子命令 create 和 delete,如何把他们组合起来呢?

import typer

import items
import users

app = typer.Typer()
app.add_typer(users.app, name="users")
app.add_typer(items.app, name="items")

if __name__ == "__main__":
app()
image-20240712112406492 image-20240712112444086

你甚至可以一直套娃下去。Do you need sub-sub-sub-subcommands? Go ahead, create all the typer.Typer()s you need and put them together with app.add_typer().

Typer applications are composable, each typer.Typer() can be a CLI app by itself, but it can also be added as a command group to another Typer app.

SubCommands in a Single File

如果必须这么做的话,你也可以在一个文件里定义一堆子命令组……不过不推荐就是了

import typer

app = typer.Typer()
items_app = typer.Typer()
app.add_typer(items_app, name="items")
users_app = typer.Typer()
app.add_typer(users_app, name="users")


@items_app.command("create")
def items_create(item: str):
print(f"Creating item: {item}")


@items_app.command("delete")
def items_delete(item: str):
print(f"Deleting item: {item}")


@items_app.command("sell")
def items_sell(item: str):
print(f"Selling item: {item}")


@users_app.command("create")
def users_create(user_name: str):
print(f"Creating user: {user_name}")


@users_app.command("delete")
def users_delete(user_name: str):
print(f"Deleting user: {user_name}")


if __name__ == "__main__":
app()

Sub Callback

子命令也能拥有属于自己的 callback!

It can handle any CLI parameters that go before its own commands and execute any extra code:

import typer

app = typer.Typer()

users_app = typer.Typer()
app.add_typer(users_app, name="users")


@users_app.callback()
def users_callback():
print("Running a users command")


@users_app.command()
def create(name: str):
print(f"Creating user: {name}")


if __name__ == "__main__":
app()

In this case it doesn't define any CLI parameters, it just writes a message.

当然也可以在实例化 typer.Typer 的时候填 callback 进去,it's just another place to add the callback.

def users_callback():
print("Running a users command")


users_app = typer.Typer(callback=users_callback)
app.add_typer(users_app, name="users")

甚至,你可以在 app.add_typer() 的时候写 callback

def callback_for_add_typer():
print("I have the high land! Running users command")


app.add_typer(users_app, name="users", callback=callback_for_add_typer)

优先级从高到低:

  1. app.add_typer(callback)
  2. @users_app.callback()
  3. typer.Typer(callback=users_callback)

help

可以在 app.add_typer 的时候写入注释说明

app = typer.Typer()

users_app = typer.Typer()
app.add_typer(users_app, name="users", help="Manage users in the app.")
image-20240712143255262

当然在其他地方也可以写入注释说明,他们的优先级和 callback 是一样的:

  1. app.add_typer(help)
  2. app.add_typer(callback=some_function)
  3. @sub_app.callback()
  4. sub_app = typer.Typer(callback=some_function)

Recap

The precedence to generate a command's name and help, from lowest priority to highest, is:

  • Implicitly inferred from sub_app = typer.Typer(callback=some_function)
  • Implicitly inferred from the callback function under @sub_app.callback()
  • Implicitly inferred from app.add_typer(sub_app, callback=some_function)
  • Explicitly set on sub_app = typer.Typer(name="some-name", help="Some help.")
  • Explicitly set on @sub_app.callback("some-name", help="Some help.")
  • Explicitly set on app.add_typer(sub_app, name="some-name", help="Some help.")

So, app.add_typer(sub_app, name="some-name", help="Some help.") always wins.